#include "ConstructorBuilder.h"
#include "ScriptCompiler/CompilerContext.h"

#include "ScriptCompiler/AST/ConstructorNode.h"
#include "ScriptCompiler/AST/SentenceBlockNode.h"

#include "ScriptCompiler/ASTBuilder/SentenceBlockBuilder.h"
#include "ScriptCompiler/ASTBuilder/ExpressionBuilder.h"
#include "ScriptCompiler/ASTBuilder/FunctionParameterBuilder.h"
#include "ScriptCompiler/ASTBuilder/ArgumentsBuilder.h"
#include "ScriptCompiler/ASTBuilder/FunctionContentBuilder.h"

CConstructorBuilder::CConstructorBuilder(std::shared_ptr<CScriptFileContext> InContext)
{
    Context = InContext;
    Result = std::make_shared<CConstructorNode>(Context);
}

bool CConstructorBuilder::ReadCurentToken()
{
    //read name 
    {
        auto Token = Context->GetCurrentToken();
        if (Token->Type != EScriptTokenType::Identifier)
        {
            Context->RaiseError(10021,Token, U"Expect identifier here");
            return false;
        }
        Result->Name = Token;
        Context->CurrentTokenIndex++;
    }


    //skip '('
    {
        auto Token = Context->GetCurrentToken();
        if (Token->Type != EScriptTokenType::LeftParentheses)
        {
            Context->RaiseError(10022,Token, U"Expect '(' here");
            return false;
        }
        Context->CurrentTokenIndex++;
    }

    //read parameters
    {

        auto RightParenthesesToken = Context->GetCurrentToken();
        while(RightParenthesesToken->Type != EScriptTokenType::RightParentheses)
        {
            auto ParameterBuilder = std::make_shared<CFunctionParameterBuilder>(Context);
            if (!ParameterBuilder->ReadCurentToken())
            {
                return false;
            }

            Result->Parameters.Add(ParameterBuilder->Result);

            auto CommaToken = Context->GetCurrentToken();
            if (CommaToken->Type == EScriptTokenType::Comma)
            {
                Context->CurrentTokenIndex++; 
            }
            else if (CommaToken->Type == EScriptTokenType::RightParentheses)
            {
                break;
            }
            else
            {
                Context->RaiseError(10023,CommaToken,U"Expected comma or right parenthesis after parameter");
                return false;
            }

            RightParenthesesToken = Context->GetCurrentToken();
        }
    }

    //skip ')'
    {
        Context->CurrentTokenIndex++;
    }

    //read : or {
    {
        auto Token = Context->GetCurrentToken();
        if (Token->Type == EScriptTokenType::Colon)
        {
            //skip ':'
            Context->CurrentTokenIndex++;

            //read base keyword
            {
                auto Token = Context->GetCurrentToken();
                if (Token->Type != EScriptTokenType::Base)
                {
                    Context->RaiseError(10024,Token, U"Expect 'base' here");
                    return false;
                }

                Context->CurrentTokenIndex++;
            }

            //skip '('
            {
                auto Token = Context->GetCurrentToken();
                if (Token->Type != EScriptTokenType::LeftParentheses)
                {
                    Context->RaiseError(10025,Token, U"Expect '(' here");
                    return false;
                }
                Context->CurrentTokenIndex++;
            }

            //read base arguments
            {
                auto ArgumentBuilder = std::make_shared<CArgumentsBuilder>(Context);
                if (!ArgumentBuilder->ReadCurentToken())
                {
                    return false;
                }

                Result->BaseConstructorArguments = ArgumentBuilder->Result;

            }
        }
      

    }

    //check if have content
    {
        auto Token = Context->GetCurrentToken();
        if (Token->Type == EScriptTokenType::Semicolon)
        {
            //this is a declaration, no content
            Context->CurrentTokenIndex++; //skip semicolon
            return true;
        }
    }

    {
        auto LambdaToken = Context->GetCurrentToken();

        switch (LambdaToken->Type)
        {
            case EScriptTokenType::LeftBrace:
            case EScriptTokenType::LambdaOperator:
            {
                auto ContentBuilder = std::make_shared<CFunctionContentBuilder>(Context);
                if (!ContentBuilder->ReadCurentToken())
                {
                    return false;
                }
                Result->Content = ContentBuilder->Result;
                return true;
            }

            default:
            {
                Context->RaiseError(10026,LambdaToken,U"Expected ';' or '{' or '=>' after function parameters");
                return false;
            }
        }
    }

    return true;
}
